package ui.server;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.packet.VCard;

import ui.client.Group;
import ui.client.GroupEntry;
import ui.client.InstantMessage;
import ui.client.Status;
import ui.client.UserPresence;


/**
 * Implementation of XMPP protocol manager
 * 
 * @author hAmmeRoL
 * 
 */
public class XMPPProtocolManager extends AbstractProtocolManager implements
		PacketListener, RosterListener {

	// XMPP connection
	private XMPPConnection conn;

	// filter only the Message class
	private PacketFilter filter = new PacketTypeFilter(Message.class);

	// XMPP user name DOES NOT contain domain name. e.g. hAmmeRoL
	private String userName;

	// domain name of XMPP protocol. e.g. gmail.com
	private String domainName;

	// hashmap containing all the chat objects
	private HashMap<String, Chat> chatMap;

	// VCard for XMPP protocol
	private VCard vCard;

	/**
	 * Construct a new XMPPProtocolManager
	 * 
	 * @param userName
	 *            the user id
	 * @param password
	 *            the password
	 * @param domainName
	 *            the domain name
	 * @param hostName
	 *            the host name
	 * @param port
	 *            the port of the host
	 */
	public XMPPProtocolManager(String userName, String password,
			String domainName, String hostName, int port) {
		this.userName = userName;
		this.password = password;
		this.domainName = domainName;
		// hAmmeRoL + @ + gmail.com
		this.userID = userName + "@" + domainName;
		this.hostName = hostName;
		this.port = port;
		chatMap = new HashMap<String, Chat>();
		this.vCard = new VCard();
	}

	public void connect() throws ProtocolException {
		ConnectionConfiguration config = new ConnectionConfiguration(hostName,
				port, domainName);
		conn = new XMPPConnection(config);
		try {
			conn.connect();
		} catch (XMPPException e) {
			throw new ProtocolException(e.toString());
		}
	}

	public void login() throws ProtocolException {
		conn.addPacketListener(this, filter);
		try {
			// login
			conn.login(userName, password, "Yaim", true);
		} catch (XMPPException e) {
			throw new ProtocolException(e.toString());
		}
	}

	public void close() {
		conn.disconnect();
	}

	public void sendMessage(InstantMessage message) throws ProtocolException {
		Chat chat;

		// if already have chat object
		if (chatMap.containsKey(message.getDestinationID())) {
			chat = chatMap.get(message.getDestinationID());
		} else {
			// construct a new one
			chat = conn.getChatManager().createChat(message.getDestinationID(),
					null);
			chatMap.put(message.getDestinationID(), chat);
		}
		try {
			chat.sendMessage(message.getContent());
		} catch (XMPPException e) {
			throw new ProtocolException(e.toString());
		}
	}

	/**
	 * Process incoming packet
	 */
	public void processPacket(Packet packet) {
		// now process only message packet
		Message message = (Message) packet;

		String content = message.getBody();

		String id = message.getFrom();

		// get the actual id
		int index = id.indexOf("/");
		String from = id.substring(0, index);

		InstantMessage temp = new InstantMessage(from, userID, content);

		eventHandler.messageArrived(temp);
	}

	@Override
	public ArrayList<Group> getGroups() {
		// get all entries
		Roster roster = conn.getRoster();
		// add listener to this
		roster.addRosterListener(this);

		Collection<RosterEntry> entries = roster.getEntries();
		HashMap<String, Group> groups = new HashMap<String, Group>();
		for (RosterEntry entry : entries) {
			Collection<RosterGroup> newGroups = entry.getGroups();
			Presence presence = roster.getPresence(entry.getUser());
			Status status;

			// get status
			if (presence.getType() == Presence.Type.unavailable) {
				status = Status.OFFLINE;
			} else {
				status = Status.ONLINE;
			}
			UserPresence newPresence = new UserPresence(entry.getUser(), status);

			// entry in unfiled group
			if (newGroups.size() == 0) {
				// unfiled group
				Group unfiled = new Group();
				if (groups.containsKey(unfiled.getName())) {
					Group group = groups.get(unfiled.getName());
					GroupEntry groupEntry = new GroupEntry(entry.getName(),
							entry.getUser(), group.getName(), newPresence);
					group.addGroupEntry(groupEntry);
				} else {
					Group group = new Group(unfiled.getName());
					GroupEntry groupEntry = new GroupEntry(entry.getName(),
							entry.getUser(), group.getName(), newPresence);
					group.addGroupEntry(groupEntry);
					groups.put(group.getName(), group);

				}
			} else {
				for (RosterGroup newGroup : newGroups) {
					if (groups.containsKey(newGroup.getName())) {
						Group group = groups.get(newGroup.getName());
						GroupEntry groupEntry = new GroupEntry(entry.getName(),
								entry.getUser(), group.getName(), newPresence);
						group.addGroupEntry(groupEntry);
					} else {
						Group group = new Group(newGroup.getName());
						GroupEntry groupEntry = new GroupEntry(entry.getName(),
								entry.getUser(), group.getName(), newPresence);
						group.addGroupEntry(groupEntry);
						groups.put(group.getName(), group);

					}
				}
			}
		}
		return new ArrayList<Group>(groups.values());

	}

	@Override
	public byte[] getAvatar(String clientID) throws ProtocolException {

		try {
			vCard.load(conn, clientID); // load someone's VCard
		} catch (XMPPException e) {
			throw new ProtocolException(e.toString());
		}
		return vCard.getAvatar();

	}

	
	public void run() {
		try {
			connect();
		} catch (ProtocolException e) {
			eventHandler.connectFailed();
		}
		try {
			login();
		} catch (ProtocolException e) {
			eventHandler.loginFailed();
		}
		eventHandler.loggedIn();

	}

	
	public void entriesAdded(Collection<String> arg0) {
		// TODO Auto-generated method stub

	}


	public void entriesDeleted(Collection<String> arg0) {
		// TODO Auto-generated method stub

	}

	
	public void entriesUpdated(Collection<String> arg0) {
		// TODO Auto-generated method stub

	}

	
	public void presenceChanged(Presence presence) {
		String from = presence.getFrom();

		System.out.println(from);

		Status status;
		if (presence.getType() == Presence.Type.available) {
			status = Status.ONLINE;
		} else {
			status = Status.OFFLINE;
		}

		if (eventHandler != null) {
			eventHandler.presenceChanged(new UserPresence(from, status));
		}

	}

}
